#include <linux/module.h>
#include <linux/videodev2.h>
#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-common.h>

//最主要的目的就是注册生成一个/dev/video0设备然后通过应用程序读取
// 声明结构体sv供v4l2_device_register使用
struct sv{
    struct v4l2_device v4l2_dev;
    struct video_device vdev;
}; 
 
static struct sv sdev;
// 查询设备支持的功能  函数函数
static int sv_querycap(struct file *file, void *priv, struct v4l2_capability *vcap)
{
    struct sv *sv = video_drvdata(file);
    strlcpy(vcap->driver, sv->vdev.name,sizeof(vcap->driver));
    strlcpy(vcap->card, "Stanway test card",sizeof(vcap->card));
    strlcpy(vcap->bus_info, "Stanway test bus",sizeof(vcap->bus_info));
    vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_READWRITE; // report capabilities
    printk(KERN_INFO "[stanway]%s, %d, \n", __func__,__LINE__);
    return 0;
}
 
/***********************************************
 *
 *V4L2模块函数
 *应用操作的read与open操作均来于此
 **********************************************/
 
static const struct v4l2_file_operations sv_fops = {
    .owner = THIS_MODULE,
    .open = v4l2_fh_open, // open /dev/video0
    .release = v4l2_fh_release, // close /dev/video
    .unlocked_ioctl = video_ioctl2,
};
 
 //video中回调集合
static const struct v4l2_ioctl_ops sv_ioctl_ops = {
    .vidioc_querycap = sv_querycap, // ioctl VIDIOC_QUERYCAP 时会调用sv_querycap函数
};
 
static int __init vivi_init(void)
{
    struct sv *sv;
    struct v4l2_device *v4l2_dev;
    int ret;
 
    sv = &sdev;
    v4l2_dev = &sv->v4l2_dev;
    //init v4l2 name, version
    strlcpy(v4l2_dev->name, "sv", sizeof(v4l2_dev->name));
    v4l2_info(v4l2_dev, "Color SV VGA driver %s\n", "0.0.1"); //output V4l2 info
    ret = v4l2_device_register(NULL, v4l2_dev);
    if (ret < 0)
    {
      printk(KERN_INFO "Could not register v4l2_device\n");
      return ret;
    }
 
    //setup video
    strlcpy(sv->vdev.name, "My vivi driver", sizeof(sv->vdev.name));
    sv->vdev.v4l2_dev = v4l2_dev; // set v4l2_device address to video_device
    sv->vdev.fops = &sv_fops; // v4l2_file_operations
    sv->vdev.ioctl_ops = &sv_ioctl_ops; // v4l2_ioctl_ops
    sv->vdev.release = video_device_release_empty;
    set_bit(V4L2_FL_USES_V4L2_FH, &sv->vdev.flags);
    video_set_drvdata(&sv->vdev, sv); //将sv设置为驱动私有数据
 
    if (video_register_device(&sv->vdev, VFL_TYPE_GRABBER, -1) != 0){
        printk(KERN_INFO "[stanway%s, %d, video_register_device FAIL\n", __func__,__LINE__);
        ret = -ENODEV;
        goto out_dev;
    }
    printk(KERN_INFO "[stanway]%s, %d, module inserted\n", __func__, __LINE__);
    return 0;
 
out_dev:
    v4l2_device_unregister(&sv->v4l2_dev);
    video_unregister_device(&sv->vdev);
    return ret;
}
 
static void __exit vivi_exit(void)
{
    struct sv *sv;
    sv = &sdev;
 
    printk(KERN_INFO "[stanway] %s, %d, module remove\n", __func__, __LINE__);
    video_unregister_device(&sv->vdev);
    v4l2_device_unregister(&sv->v4l2_dev);
}
 
module_init(vivi_init);
module_exit(vivi_exit);
MODULE_LICENSE("GPL");
